package model.skill;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import main.Log;
import model.Executable;
import model.GameClock;
import model.effect.ConstantChangeStatEffect;
import model.effect.Effect;
import model.entity.stats.StatType;
import model.map.Direction;
import model.map.GameMap;
import model.map.HexLocationProjection;
import model.map.Location;
import model.map.LocationProjection;
import model.map.World;

/**
 * A propagation to return all the locations 
 */
public class AngularProjectile extends Projectile{

	/**
	 * @param startLocation
	 * @param range
	 * @param direction
	 * @param propagationLogic
	 */
	
	private int counter;
	private int range;
	private Location tempLocation;
	private Set<Location> locations; 
	private Set<Location> previousMapLocations;
	private Location movedStartLocation;
	private AngularProjectile angular = this;
	
	public AngularProjectile(String name, Effect effect, LocationProjection propagationLogic, int range, int speed)
	{
		super(name, effect, propagationLogic, range, speed);
	}
	
	public AngularProjectile() {
		// TODO Auto-generated constructor stub
	}

	public void propagate(ProjectileInformation projectileInfo)
	{
		locations = new HashSet<Location>();
		movedStartLocation = projectileInfo.getLocation();
		tempLocation = new Location(0,0);
		counter = 0;
		range = super.getRange() - 1;
		
		final GameMap gameMap = World.getInstance().getCurrentMap();
		final GameClock c = GameClock.getInstance();
		final LocationProjection mySuper = this.getPropagationLogic();
		final int propagateSpeed = this.getSpeed();
		final Direction projectileDirection = projectileInfo.getDirection();
		
		Executable exec = new Executable()
		{
			public void execute()
			{
				if(previousMapLocations != null)
				{
					Iterator<Location> previousMapLocationsIT = previousMapLocations.iterator();
					while(previousMapLocationsIT.hasNext())
					{
						Location mapLocation = previousMapLocationsIT.next();
						if(gameMap.hasProjectile(mapLocation))
						{
							gameMap.removeProjectile(mapLocation);
						}
					}
				}
				
				movedStartLocation = mySuper.projectLocation(movedStartLocation, projectileDirection, 1);
				if(tempLocation.equals(movedStartLocation))
				{
					counter = range;
					switch(projectileDirection)
					{
						case S:
						{
							tempLocation = new Location(movedStartLocation.getX() + 1, movedStartLocation.getY() + 1);
							break;
						}
						
						case SE:
						{
							tempLocation = new Location(movedStartLocation.getX(), movedStartLocation.getY() + 1);
							break;
						}
						
						case NE:
						{
							tempLocation = new Location(movedStartLocation.getX() - 1, movedStartLocation.getY());
							break;
						}
						
						case N:
						{
							tempLocation = new Location(movedStartLocation.getX() - 1, movedStartLocation.getY() - 1);
							break;
						}
						
						case NW:
						{
							tempLocation = new Location(movedStartLocation.getX(), movedStartLocation.getY() - 1);
							break;
						}
						case SW:
						{
							tempLocation = new Location(movedStartLocation.getX() + 1, movedStartLocation.getY());
							break;
						}
					}
				}
				else
				{
					locations.add(movedStartLocation);
					tempLocation = new Location(movedStartLocation.getX(), movedStartLocation.getY());
				}
				
				for(int j = 0; j < Math.ceil((double)(counter+1)/2); ++j)
				{
					int offset = j+1;
					
					switch(projectileDirection)
					{
						case S:
						{
							Location temp = new Location(tempLocation.getX(), tempLocation.getY() - offset);
							if((!temp.equals(tempLocation)) && isValidLocation(temp))
							{
								locations.add(temp);
							}
							temp = new Location(tempLocation.getX() - offset, tempLocation.getY());
							if((!temp.equals(tempLocation)) && isValidLocation(temp))
							{
								locations.add(temp);
							}
							break;
						}
						
						case SE:
						{
							Location temp = new Location(tempLocation.getX() - offset, tempLocation.getY() - offset);
							if((!temp.equals(tempLocation)) && isValidLocation(temp))
							{
								locations.add(temp);
							}
							temp = new Location(tempLocation.getX() + offset, tempLocation.getY());
							if((!temp.equals(tempLocation)) && isValidLocation(temp))
							{
								locations.add(temp);
							}
							break;
						}
						
						case NE:
						{
							Location temp = new Location(tempLocation.getX(), tempLocation.getY() - offset);
							if((!temp.equals(tempLocation)) && isValidLocation(temp))
							{
								locations.add(temp);
							}
							temp = new Location(tempLocation.getX() + offset, tempLocation.getY() + offset);
							if((!temp.equals(tempLocation)) && isValidLocation(temp))
							{
								locations.add(temp);
							}
							break;
						}
						
						case N:
						{
							Location temp = new Location(tempLocation.getX(), tempLocation.getY() + offset);
							if((!temp.equals(tempLocation)) && isValidLocation(temp))
							{
								locations.add(temp);
							}
							temp = new Location(tempLocation.getX() + offset, tempLocation.getY());
							if((!temp.equals(tempLocation)) && isValidLocation(temp))
							{
								locations.add(temp);
							}
							break;
						}
						
						case NW:
						{ //TODO TDA GALORE
							Location temp = new Location(tempLocation.getX() - offset, tempLocation.getY());
							if((!temp.equals(tempLocation)) && isValidLocation(temp))
							{
								locations.add(temp);
							}
							temp = new Location(tempLocation.getX() + offset, tempLocation.getY() + offset);
							if((!temp.equals(tempLocation)) && isValidLocation(temp))
							{
								locations.add(temp);
							}
							break;
						}
						
						case SW:
						{
							Location temp = new Location(tempLocation.getX(), tempLocation.getY() + offset);
							if((!temp.equals(tempLocation)) && isValidLocation(temp))
							{
								locations.add(temp);
							}
							temp = new Location(tempLocation.getX() - offset, tempLocation.getY() - offset);
							if((!temp.equals(tempLocation)) && isValidLocation(temp))
							{
								locations.add(temp);
							}
							break;
						}
						default: Log.out(Log.MODEL, "Angular: returned NULL");
					}
				}
				
				Iterator<Location> newMapLocationsIT = locations.iterator();
				while(newMapLocationsIT.hasNext())
				{
					gameMap.addProjectile(newMapLocationsIT.next(), angular);
				}
				
				newMapLocationsIT = locations.iterator();
				Location checker;
				while(newMapLocationsIT.hasNext())
				{
					checker = newMapLocationsIT.next();
					if(gameMap.hasEntity(checker))
						angular.getEffect().doEffect(gameMap.getEntity(checker));
				}
				
				previousMapLocations = locations;
				
				locations = new HashSet<Location>();
				if(counter < range) 
				{
					++counter;
					c.schedule(propagateSpeed, this);
				}
				
				else
				{
					c.schedule(propagateSpeed, new Executable() {
						public void execute() 
						{	
							Iterator<Location> previousMapLocationsIT = previousMapLocations.iterator();
							while(previousMapLocationsIT.hasNext())
							{
								Location mapLocation = previousMapLocationsIT.next();
								if(gameMap.hasProjectile(mapLocation))
								{
									gameMap.removeProjectile(mapLocation);
								}
							}
						}
					});
				}
			}
		};
		exec.execute();
	}
	
	private boolean isValidLocation(Location location)
	{
		return World.getInstance().currentMapExists(location);
	}
}
